/*
 * 文 件 名:  SqlUtils.java
 * 版    权:  winningsoft. Copyright YYYY-YYYY,  All rights reserved
 * 描    述:  <描述>
 * 修 改 人:  项鸿铭
 * 修改时间:  2016-6-17
 * 跟踪单号:  <跟踪单号>
 * 修改单号:  <修改单号>
 * 修改内容:  <修改内容>
 */
package com.winning.common.tools;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;

import com.framework.basedata.WebConstant;
import com.framework.helper.ServerSQLExecutorUtils;
import com.winning.common.AppContext;
import com.winning.common.Constans;


/**
 * 类名称：SQLTool
 * 类描述：SQL类的工具类
 * 创建人：liuwensheng
 * 创建时间：2016-12-13 上午8:53:02
 * 修改人：liuwensheng
 * 修改时间：2016-12-13 上午8:53:02
 * 修改备注：
 */
public class SQLTool {
    public static String DB_SQL_SELECT_UPPER_VALUE = "SELECT";
    public static String DB_SQL_SELECT_LOWER_VALUE = "select";

    public static String DB_SQL_AS_UPPER_VALUE = " AS";
    public static String DB_SQL_AS_LOWER_VALUE = "as";

    public static String DB_SQL_COLUMN_TBRQ_UPPER_VALUE = "_TBRQ";

    /**
     * 临时封装
     * <一句话功能简述>
     * <功能详细描述>
     *
     * @param jdbcTemplate
     * @param sql
     * @param objs
     * @return
     * @throws Exception
     * @see [类、类#方法、类#成员]
     */
    public static List<HashMap<String, String>> getResult(String sql, String[] objs) throws Exception {
        StringBuffer sb = new StringBuffer(" set nocount on  ");
        sb.append(sql);
        sb.append("  set nocount off ");
        ResultSet rs = null;
        List<HashMap<String, String>> list = new ArrayList<HashMap<String, String>>();
        try {
            rs = ServerSQLExecutorUtils.execProc(sb.toString(), objs, WebConstant.SQLSERVER_DB);
            ResultSetMetaData rsmd = rs.getMetaData();
            int column = rsmd.getColumnCount();
            while (rs.next()) {
                LinkedHashMap<String, String> hsmap = new LinkedHashMap<String, String>();
                for (int i = 0; i < column; i++) {
                    String colname = rsmd.getColumnName(i + 1);
                    //bug rs.getObject(colname)为空未判断
                    String parameterObject = "";
                    if (rs.getObject(colname) != null) {
                        parameterObject = rs.getObject(colname).toString();
                    }
                    hsmap.put(colname, parameterObject);
                }
                list.add(hsmap);
            }
        } finally {
            if (rs != null) {
                rs.close();
            }
        }

        return list;
    }

    /**
     * 根据增量更新时间，给基础SQL增加增量更新条件 <br/>
     * 方法名称: createIncreaseSQL
     *
     * @param serviceID            标识唯一一个接口的ID
     * @param basicSQL             配置的基础SQL
     * @param tableValueMapFromSQL 从SQL中提取的做增量操作的业务表名称，
     * @param tableValueMapFromDB  增量更新时间列表，从数据库SYS_SJSC_TABLE中获取的
     * @return String 返回一个标准的SQL，增加了增量更新时间<br/>
     * @throws <br/>
     * @author liuwensheng@winning.com.cn<br/>
     * 创建时间：2016-12-13/上午9:28:45<br/>
     */
    public static String createIncreaseSQL(String serviceID, String basicSQL, HashMap<String, String> tableValueMapFromSQL, HashMap<String, String> tableValueMapFromDB) {
        String result = "";

        String basicNewSQL = "SELECT * FROM (" + basicSQL + "  ) TEMP_TABLE ";

        //根据从SQL中提取的表名称，到缓存中查找表更新的缓存时间
        String whereSQL = " WHERE ";

        if (tableValueMapFromSQL != null && tableValueMapFromSQL.size() > 0) {
            for (Entry<String, String> entry : tableValueMapFromSQL.entrySet()) {
                String tableName = entry.getKey();

                //根据接口ID（手动配置的）和业务表名称，组合一个规则表达式:serviceID%tabelName;
                String mapKey = getTableMapKey(tableName, serviceID);

                //时间格式统一为YYYY-MM-DD hh:mm:ss
                String tableTime = tableValueMapFromDB.get(mapKey) == null ? "1900-01-01 00:00:00" : tableValueMapFromDB.get(mapKey);

                //whereSQL += tableName + DB_SQL_COLUMN_TBRQ_UPPER_VALUE + " > to_date('" + tableTime + "','YYYY-MM-DD HH24:MI:SS') OR ";
                //修改条件,并且将从数据库中取出的时间的最后(.0)去除
                whereSQL += tableName + DB_SQL_COLUMN_TBRQ_UPPER_VALUE + " > '" + tableTime.replaceAll("\\.0", "") + "' OR ";

            }

            whereSQL = whereSQL.substring(0, whereSQL.length() - 3);

            //拼接条件
            result = basicNewSQL + whereSQL;
        } else {
            result = basicNewSQL;
        }

        return result;
    }

    /**
     * 填报日期返回字段命名规则：tableName_TBRQ
     * 从按照统一规则编写的SQL，截取出业务表名称
     *
     * @param sqlValue
     * @return
     */
    public static HashMap<String, String> getTableNameFromSQL(String sqlValue) {
        HashMap<String, String> tableValueMap = new HashMap<String, String>();

        if (sqlValue == null) {
            return tableValueMap;
        }

        //统一转换为大写
        String sqlUpperValue = sqlValue.toUpperCase();//不合理

        //从第一个SELECT/select 处开始截取
        int index = sqlUpperValue.indexOf(DB_SQL_SELECT_UPPER_VALUE) + 6;

        if (index > -1) {
            String subStr = sqlUpperValue.substring(index);

            //按照统一的命名规则截取，填报日期字段是所有业务表一致存在的。按照表名_填报日期（tableName_TBRQ）返回填报日期
            int tbrqIndex = subStr.indexOf(DB_SQL_COLUMN_TBRQ_UPPER_VALUE);

            //只有SQL中存在TBRQ这个字段后，才做提取操作
            if (tbrqIndex > -1) {
                String[] aList = subStr.split(DB_SQL_COLUMN_TBRQ_UPPER_VALUE);

                //最后一个，既其他查询返回字段值不需要
                for (int i = 0; i < aList.length - 1; i++) {
                    String splitStrValue = aList[i] == null ? "" : aList[i];

                    String tableNameForAS = "";
                    if (splitStrValue.indexOf(",") > -1) {
                        //以,分隔符截取，取数组中最后一个值
                        String[] tableNames = splitStrValue.split(",");

                        tableNameForAS = tableNames[tableNames.length - 1];
                    } else {
                        tableNameForAS = splitStrValue;
                    }

                    int asIndex = tableNameForAS.indexOf(DB_SQL_AS_UPPER_VALUE);

                    if (asIndex > -1) {
                        //提取表名称, 以AS为标准
                        String[] tableNameList = tableNameForAS.split(DB_SQL_AS_UPPER_VALUE);

                        for (int tableNameIndex = 1; tableNameIndex < tableNameList.length; tableNameIndex++) {
                            String tableName = tableNameList[tableNameIndex];

                            if (null != tableName && tableName.length() > 0) {
                                //将表名称转换为大写
                                tableValueMap.put(tableName.trim().toUpperCase(), "");
                            }
                        }
                    }
                }
            }
        }

        return tableValueMap;
    }

    /**
     * 统一将tableName转换为跟业务接口绑定的新Key：serviceID%tabelName;
     *
     * @param tableName
     * @return
     */
    public static String getTableMapKey(String tableName, String serviceID) {
        String newKeyStr = serviceID + "%" + tableName;

        return newKeyStr.toUpperCase();
    }

    /**
     * @param @param  serviceID
     * @param @param  tableValueMapFromSQL
     * @param @return
     * @return String
     * @throws
     * @Title: getTableDateName
     * @Description: 从SQL中提取时间字段
     */
    public static String getTableDateName(String serviceID, HashMap<String, String> tableValueMapFromSQL) {
        String result = null;

        if (tableValueMapFromSQL != null && tableValueMapFromSQL.size() > 0) {
            for (Entry<String, String> entry : tableValueMapFromSQL.entrySet()) {
                String tableName = entry.getKey();

                //按照统一规则
                result = tableName + DB_SQL_COLUMN_TBRQ_UPPER_VALUE;
            }
        }

        return result;
    }

    /**
     * @param @param  serviceID            接口ID
     * @param @param  sql                  查询用SQL
     * @param @param  upperColumnNameStr   本批次中一行记录中的一列数据的字段名称
     * @param @param  columnValueStr       本批次中一行记录中的一列数据的值
     * @param @throws ParseException
     * @return void
     * @throws
     * @Title: sortDataTimeToLocal
     * @Description: 逐行筛选业务数据的填报日期，包括多表关联情况，将本批次中最大的填报日期缓存到本地，当业务表数据全部上传完毕后，再更新中间表SYS_SJSC_TABLE
     */
    public static void sortDataTimeToLocal(String serviceID, String sql, String upperColumnNameStr, String columnValueStr) throws ParseException {
        //根据查询SQL，提取业务表名称和时间戳字段_TBRQ,从SYS_SJSC_TABLE中查询表的更新时间
        HashMap<String, String> tableValueMapFromSQL = getTableNameFromSQL(sql);

        for (Entry<String, String> entry : tableValueMapFromSQL.entrySet()) {
            String tableName = entry.getKey();
            //LOGGER.info("--> [" + serviceBeanVo.getInfoName() + "], upperColumnNameStr：" + upperColumnNameStr + "; test:" + tableName +  Contans.DB_SQL_COLUMN_TBRQ_UPPER_VALUE);
            // 每一条业务数据的最新上报日期,需要和其他数据比较大小，从本批次数据中得到最近的日期
            if (upperColumnNameStr.equals(tableName + DB_SQL_COLUMN_TBRQ_UPPER_VALUE)) {
                String mapKey = getTableMapKey(tableName, serviceID);

                if (columnValueStr.indexOf(":") == -1 && columnValueStr.length() == 10) {
                    columnValueStr += " 00:00:00";
                }

                //得到业务数据值
                String lastTimeValueFromDB = columnValueStr;


                //得到本地缓存的时间值，缓存的时间是业务数据中最大的填报日期，缓存所有数据
                HashMap<String, String> lastTimeValueMap = AppContext.getInstance().getTableLastTimeMapLocal();

                String lastTimeValueFromLocal = lastTimeValueMap.get(mapKey);

                /*
                 * 注意：由于存在公用业务表，如A表，会在多个接口中重复使用。假如目前有接口1和接口2，接口1需要关联A+B两表数据，接口2需要关联A+C两表数据
                 *      如果接口1先更新了表A的填报日期为最新的2014-10-1，之间存在2014-9-1、2014-9-21等数据，这时中间表A的更新时间被修改2014-10-1。
                 *      到接口2查询A表数据时，从中间表提取的更新日期已经是2014-10-1，则导致接口2无法提取A表中的2014-9-1、2014-9-21等数据。
                 * 所以，综合以上案例，需要将中间表中的业务表名，转换为和具体接口绑定的关系
                 */
                if (null == lastTimeValueFromLocal || "".equals(lastTimeValueFromLocal)) {
                    //key需要做转换：serviceID%tableName,本地没有缓存最新的日期，初始化更新日期为最新的
                    lastTimeValueMap.put(mapKey, lastTimeValueFromDB);
                } else {
                    DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

                    Date lastTimeValueFromDBDate = dateFormat.parse(lastTimeValueFromDB);
                    Date lastTimeValueFromLocalDate = dateFormat.parse(lastTimeValueFromLocal);

                    //将数据库中得业务数据中的填报日期 和 本地缓存的最新的日期比较大小，最近的日期为最大值，则本地更新日期为最大值
                    if (lastTimeValueFromDBDate.getTime() > lastTimeValueFromLocalDate.getTime()) {
                        lastTimeValueMap.put(mapKey, lastTimeValueFromDB);
                    }
                }
            }
        }
    }

}
